#pragma once
#ifndef OBR_INTERACTIONDATA_H
#define OBR_INTERACTIONDATA_H

#include "DScommon.h"
#include "RayData.h"

namespace OBR
{
enum class InteractionType
{
    SURFACE_INTERACTION,
    MEDIUM_INTERACTION
};
/**
 * @brief surface interaction returned from MeshDC
 *
 */
struct InteractionData
{
    RayData wi, wo;
    float3 n;
    float3 world_pos;
    float3 obj_pos;
    float2 uv;
    InteractionType type;
    /* ---------------------------- differentials --------------------------- */
    bool has_differentials = false;
    float3 dndu, dndv;
    float3 dpdu, dpdv;
    float3 dpdx, dpdy;
    float dudx, dudy, dvdx, dvdy;
    /* ---------------------------- material info --------------------------- */
    int mat_id = -1;
    int mat_instance_id;
    /* ----------------------------- medium info ---------------------------- */
    int med_id_inside = -1;
    int med_id_outside = -1;
    int med_instance_id_inside;
    int med_instance_id_outside;
    /* ------------------------------ mesh info ----------------------------- */
    int mesh_id = -1;
    int mesh_instance_id;
    /* ----------------------------- light info ----------------------------- */
    int light_id = -1;
    int light_instance_id;
    /* -------------------------------- print ------------------------------- */
    __forceinline__ __device__ void print()
    {
        printf("===================Surface Data===================\n");
        printf("worldpos: [%8.6f, %8.6f, %8.6f]\n", world_pos.x, world_pos.y, world_pos.z);
        printf("objpos:   [%8.6f, %8.6f, %8.6f]\n", obj_pos.x, obj_pos.y, obj_pos.z);
        printf("normal:   [%8.6f, %8.6f, %8.6f]\n", n.x, n.y, n.z);
        printf("texcoord: [%8.6f, %8.6f]\n", uv.x, uv.y);
        printf("mat: %d : %d\n", mat_id, mat_instance_id);
        printf("light: %d : %d\n", light_id, light_instance_id);
        printf("med inside: %d : %d\n", med_id_inside, med_instance_id_inside);
        printf("med outside: %d : %d\n", med_id_outside, med_instance_id_outside);
        printf("==================================================\n");
    }
    __forceinline__ __device__ RayData spawnRayTo(float3 dest)
    {
        RayData ray;
        ray.o = world_pos;
        ray.d = normalize(dest - world_pos);
        if (dot(ray.d, n) < 0)
        {
            ray.med_id = med_id_inside;
            ray.med_instance_id = med_instance_id_inside;
        }
        else
        {
            ray.med_id = med_id_outside;
            ray.med_instance_id = med_instance_id_outside;
        }
        ray.t = 0;
        ray.tmin = 1e-4;
        ray.tmax = length(dest - world_pos) - 1e-4;
        return ray;
    }
    __forceinline__ __device__ RayData spawnRay(float3 dir)
    {
        RayData ray;
        ray.o = world_pos;
        ray.d = normalize(dir);
        if (dot(dir, n) < 0)
        {
            ray.med_id = med_id_inside;
            ray.med_instance_id = med_instance_id_inside;
        }
        else
        {
            ray.med_id = med_id_outside;
            ray.med_instance_id = med_instance_id_outside;
        }
        ray.t = 0;
        ray.tmin = 1e-4;
        ray.tmax = 1e8;
        return ray;
    }
};

} // namespace OBR

#endif
